﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using AppLimit.CloudComputing.SharpBox;

namespace CipherBox
{
    public delegate void DropBoxStateChanged(TransferType t);
    public enum TransferType { Upload, Download, DeleteLocal, DeleteRemote, Pause }
    class DropBoxSynchronisation
    {
        #region Fields
        public DropBoxStateChanged _OnDropBoxStateChanged;
        protected List<string> m_LocalDirectories;
        protected List<string> m_LocalFiles;
        protected Dictionary<settingsJob, DropBox> m_jobs;
        protected Dictionary<settingsJob, XMLDirInfo.directoryinfo> m_dirinfo = new Dictionary<settingsJob, XMLDirInfo.directoryinfo>();
        protected static string DIRINFO_FILENAME = ".dirinfo";
        protected DropBoxProgressChanged _ToolTipCallback;
        #endregion Fields
        #region Properties
        TransferType m_CurrentType = TransferType.Pause;
        public TransferType CurrentType
        {
            get { return m_CurrentType; }
            set
            {
                m_CurrentType = value;
                _OnDropBoxStateChanged(m_CurrentType);
            }
        }
        #endregion Properties
        #region Constructor
        public DropBoxSynchronisation(settings settings, FileOperationProgressChanged callback, DropBoxProgressChanged toolTipCallback,DropBoxStateChanged onDropBoxStateChanged)
        {
            _OnDropBoxStateChanged = onDropBoxStateChanged;
            _ToolTipCallback = toolTipCallback;
            m_jobs = new Dictionary<settingsJob, DropBox>();
            foreach (settingsJob job in settings.job)
            {
                m_jobs.Add(job, new DropBox(job.token, File.ReadAllText(job.key), job.remotePath, callback));
                if (File.Exists(job.localPath + "\\" + DIRINFO_FILENAME))
                {
                    m_dirinfo.Add(job, XMLSerializing.Deserialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME));
                    XMLSerializing.Serialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME, m_dirinfo[job]);
                }
                else
                {
                    m_dirinfo.Add(job, new XMLDirInfo.directoryinfo());
                    XMLSerializing.Serialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME, m_dirinfo[job]);
                }
            }
            CurrentType = TransferType.Pause;
        }
        #endregion Constructor
        #region Private Methods
        protected string localToRemotePath(settingsJob job, string localPath)
        {
            string relativePath = localPath.Replace(job.localPath, string.Empty);
            return ("/" + relativePath.Replace('\\', '/')).Replace("//", "/");
        }
        protected string remoteToLocalPath(settingsJob job, string remotePath)
        {
            string relativePath = remotePath.Replace("/" + job.remotePath, string.Empty);
            return (job.localPath + relativePath.Replace("/", "\\")).Replace("/" + job.remotePath, "");
        }

        protected void registerFilePair(settingsJob job, string localPath, string remotePath)
        {
            FileInfo fi = new FileInfo(localPath);
            m_dirinfo[job].SetDirectoryInfo(localPath, fi.LastWriteTime, remotePath, m_jobs[job].GetTimestamp(remotePath));
            XMLSerializing.Serialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME, m_dirinfo[job]);
        }
        protected void unregisterFilePair(settingsJob job, string localPath)
        {
            m_dirinfo[job].DeleteFile(localPath);
            XMLSerializing.Serialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME, m_dirinfo[job]);
        }
        protected void unregisterFolder(settingsJob job, string localPath)
        {
            m_dirinfo[job].DeleteDirectory(localPath);
            XMLSerializing.Serialize<XMLDirInfo.directoryinfo>(job.localPath + "\\" + DIRINFO_FILENAME, m_dirinfo[job]);
        }
        protected void getDirectories(string path)
        {
            m_LocalDirectories.Add(path);
            m_LocalFiles.AddRange(Directory.GetFiles(path));
            string[] directories = Directory.GetDirectories(path);
            foreach (string directory in directories)
            {
                getDirectories(directory);
            }
        }
        protected void Upload(settingsJob job, string localPath, string remotePath)
        {
            CurrentType = TransferType.Upload;
            m_jobs[job].Upload(localPath, remotePath);
            registerFilePair(job, localPath, remotePath);
            CurrentType = TransferType.Pause;

        }
        protected void Download(settingsJob job, string localPath, string remotePath)
        {
            CurrentType = TransferType.Download;
            m_jobs[job].Download(localPath, remotePath);
            registerFilePair(job, localPath, remotePath);
            CurrentType = TransferType.Pause;
        }
        protected void deleteRemoteFile(settingsJob job, string file)
        {
            CurrentType = TransferType.DeleteRemote;
            m_jobs[job].DeleteFile(file);
            unregisterFilePair(job, file);
            CurrentType = TransferType.Pause;
        }

        protected void deleteRemoteDirectory(settingsJob job, string dir)
        {
            CurrentType = TransferType.DeleteRemote;
            m_jobs[job].DeleteDirectory(dir);
            unregisterFolder(job, remoteToLocalPath(job, dir));
            CurrentType = TransferType.Pause;
        }
        protected void deleteLocalFile(settingsJob job, string file)
        {
            CurrentType = TransferType.DeleteLocal;
            File.Delete(file);
            unregisterFilePair(job, file);
            CurrentType = TransferType.Pause;
        }

        protected void deleteLocalDirectory(settingsJob job, string localDirectory)
        {

            CurrentType = TransferType.DeleteLocal;
            Directory.Delete(localDirectory);
            unregisterFolder(job, localDirectory);
            CurrentType = TransferType.Pause;
        }
        #endregion Private Methods
        #region Public Methods

        public void SyncCleanOutgoing()
        {
            int deletedFiles = 0, deletedDirectories = 0;
            foreach (settingsJob job in m_jobs.Keys)
            {
                foreach (string file in m_jobs[job].AllFiles)
                {
                    if (!File.Exists(remoteToLocalPath(job, file)) && m_dirinfo[job].FileExists(remoteToLocalPath(job, file)))
                    {
                        deletedFiles++;
                        deleteRemoteFile(job, file);
                    }
                }
                string[] allDirectories = m_jobs[job].AllDirectories;
                for (int i = allDirectories.Length - 1; i >= 0; i--)
                {
                    string localPath = remoteToLocalPath(job, allDirectories[i]);
                    if (!Directory.Exists(localPath) && m_dirinfo[job].DirectoryExists(localPath))
                    {
                        deletedDirectories++;
                        deleteRemoteDirectory(job, allDirectories[i]);
                    }
                }
            }
            if (deletedFiles > 0 || deletedDirectories > 0)
            {
                string message = "Du hast ";
                if (deletedFiles > 0)
                {
                    message += deletedFiles.ToString() + " Dateien ";
                }
                if (deletedDirectories > 0 && deletedFiles > 0)
                {
                    message += "und ";
                }
                if (deletedDirectories > 0)
                {
                    message += deletedDirectories.ToString() + " Ordner ";
                }
                message += " gelöscht";
                _ToolTipCallback(message);
            }

        }
        public void SyncCleanIncoming()
        {
            int deletedFiles = 0, deletedDirectories = 0;
            foreach (settingsJob job in m_jobs.Keys)
            {
                m_LocalDirectories = new List<string>();
                m_LocalFiles = new List<string>();
                getDirectories(job.localPath);
                foreach (string file in m_LocalFiles)
                {
                    if (!file.EndsWith(DropBoxSynchronisation.DIRINFO_FILENAME))
                    {
                        if (!m_jobs[job].FileExists(localToRemotePath(job, file)) && m_dirinfo[job].FileExists(file))
                        {
                            deletedFiles++;
                            deleteLocalFile(job, file);
                        }
                    }
                }

                for (int i = m_LocalDirectories.Count - 1; i >= 0; i--)
                {
                    if (!m_jobs[job].DirectoryExists(localToRemotePath(job, m_LocalDirectories[i] + "/")) && m_dirinfo[job].DirectoryExists(m_LocalDirectories[i]))
                    {
                        deletedDirectories++;
                        deleteLocalDirectory(job, m_LocalDirectories[i]);
                    }
                }
            }
            if (deletedFiles > 0 || deletedDirectories > 0)
            {
                string message = "Es wurden ";
                if (deletedFiles > 0)
                {
                    message += deletedFiles.ToString() + " Dateien ";
                }
                if (deletedDirectories > 0 && deletedFiles > 0)
                {
                    message += "und ";
                }
                if (deletedDirectories > 0)
                {
                    message += deletedDirectories.ToString() + " Ordner ";
                }
                message += " gelöscht";
                _ToolTipCallback(message);
            }
        }

        public void SyncIncoming()
        {
            int addedFiles = 0, addedDirectories = 0, actualizedFiles = 0;
            foreach (settingsJob job in m_jobs.Keys)
            {

                m_LocalDirectories = new List<string>();
                m_LocalFiles = new List<string>();
                getDirectories(job.localPath);
                string[] allFiles = m_jobs[job].AllFiles;
                string[] allDirectories = m_jobs[job].AllDirectories;
                foreach (string directory in allDirectories)
                {
                    string localPath = remoteToLocalPath(job, directory);
                    if (!Directory.Exists(localPath) && !m_dirinfo[job].DirectoryExists(localPath))
                    {
                        addedDirectories++;

                        Directory.CreateDirectory(localPath);
                        registerFilePair(job, localPath, localToRemotePath(job, localPath));
                    }
                }

                foreach (string file in allFiles)
                {
                    if (!File.Exists(remoteToLocalPath(job, file)))
                    {
                        addedFiles++;
                        Download(job, remoteToLocalPath(job, file), file);
                    }
                    else
                    {
                        XMLDirInfo.directoryinfoSnyncfile dd = m_dirinfo[job].GetFile(remoteToLocalPath(job, file));

                        FileCompare f = m_jobs[job].CompareFiles(remoteToLocalPath(job, file), file, dd);
                        switch (f)
                        {
                            case (FileCompare.LocalIsNewer):
                                actualizedFiles++;
                                Upload(job, remoteToLocalPath(job, file), file);
                                break;
                            case (FileCompare.RemoteIsNewer):
                                addedFiles++;
                                Download(job, remoteToLocalPath(job, file), file);
                                break;
                        }
                    }
                }
            }
            if (addedFiles > 0 || addedDirectories > 0 || actualizedFiles > 0)
            {
                string message = "Es wurden ";
                if (addedFiles > 0)
                {
                    message += addedFiles.ToString() + " Dateien ";
                }
                if (addedDirectories > 0 && addedFiles > 0)
                {
                    message += "und ";
                }
                if (addedDirectories > 0)
                {
                    message += addedDirectories.ToString() + " Ordner ";
                }
                message += " hinzugefügt!\n";
                if (actualizedFiles > 0)
                {
                    message += "Du hast " + actualizedFiles.ToString() + " Dateien geändert!";
                }
                _ToolTipCallback(message);
            }
        }
        public void SyncOutgoing()
        {
            int addedFiles = 0, addedDirectories = 0, actualizedFiles = 0;
            foreach (settingsJob job in m_jobs.Keys)
            {
                m_LocalDirectories = new List<string>();
                m_LocalFiles = new List<string>();
                getDirectories(job.localPath);
                foreach (string directory in m_LocalDirectories)
                {
                    string relativePath = localToRemotePath(job, directory);
                    if (relativePath != "/")
                    {

                        if (!m_jobs[job].DirectoryExists(relativePath) && !m_dirinfo[job].DirectoryExists(directory + "\\"))
                        {
                            addedDirectories++;
                            m_jobs[job].CreateDirectory(relativePath);
                            registerFilePair(job, remoteToLocalPath(job, relativePath + "/"), relativePath + "/");

                        }
                    }
                }

                foreach (string localFile in m_LocalFiles)
                {
                    if (!localFile.EndsWith(DropBoxSynchronisation.DIRINFO_FILENAME))
                    {
                        string remoteFile = localToRemotePath(job, localFile);

                        if (remoteFile != "/")
                        {

                            if (!m_jobs[job].FileExists(remoteFile))
                            {
                                addedFiles++;
                                Upload(job, localFile, remoteFile);
                            }
                            else
                            {
                                XMLDirInfo.directoryinfoSnyncfile dd = m_dirinfo[job].GetFile(localFile);

                                FileCompare f = m_jobs[job].CompareFiles(localFile, remoteFile, dd);
                                switch (f)
                                {
                                    case (FileCompare.LocalIsNewer):
                                        addedFiles++;
                                        Upload(job, localFile, remoteFile);
                                        break;
                                    case (FileCompare.RemoteIsNewer):
                                        actualizedFiles++;
                                        Download(job, localFile, remoteFile);
                                        break;
                                }
                            }
                        }
                    }
                }
            }
            if (addedFiles > 0 || addedDirectories > 0 || actualizedFiles > 0)
            {
                string message = "Du hast ";
                if (addedFiles > 0)
                {
                    message += addedFiles.ToString() + " Dateien ";
                }
                if (addedDirectories > 0 && addedFiles > 0)
                {
                    message += "und ";
                }
                if (addedDirectories > 0)
                {
                    message += addedDirectories.ToString() + " Ordner ";
                }
                message += " hinzugefügt!\n";
                if (actualizedFiles > 0)
                {
                    message += "Ws wurden " + addedFiles.ToString() + " Dateien geändert!";
                }
                _ToolTipCallback(message);
            }

        }

        #endregion Public Methods
    }
}
